home *** CD-ROM | disk | FTP | other *** search
/ Atari Mega Archive 1 / Atari Mega Archive - Volume 1.iso / gnu / progutil / iostream.zoo / src / filebuf.cc < prev    next >
Encoding:
C/C++ Source or Header  |  1991-09-22  |  14.0 KB  |  584 lines

  1. //    This is part of the iostream library, providing input/output for C++.
  2. //    Copyright (C) 1991 Per Bothner.
  3. //
  4. //    This library is free software; you can redistribute it and/or
  5. //    modify it under the terms of the GNU Library General Public
  6. //    License as published by the Free Software Foundation; either
  7. //    version 2 of the License, or (at your option) any later version.
  8. //
  9. //    This library is distributed in the hope that it will be useful,
  10. //    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. //    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  12. //    Library General Public License for more details.
  13. //
  14. //    You should have received a copy of the GNU Library General Public
  15. //    License along with this library; if not, write to the Free
  16. //    Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  17.  
  18. #include "ioprivat.h"
  19. #include <sys/file.h>
  20. #include <sys/stat.h>
  21. #include <errno.h>
  22.  
  23. // An fstream can be in at most one of put mode, get mode, or putback mode.
  24. // Putback mode is a variant of get mode.
  25.  
  26. // In a filebuf, there is only one current position, instead of two
  27. // separate get and put pointers.  In get mode, the current posistion
  28. // is that of gptr(); in put mode that of pptr().
  29.  
  30. // The position in the buffer that corresponds to the position
  31. // in external file system is file_ptr().
  32. // This is normally egptr(), except in putback mode, when it is _save_egptr.
  33. // If the field _fb._offset is >= 0, it gives the offset in
  34. // the file as a whole of the start of the buffer (base()).
  35.  
  36. // PUT MODE:
  37. // If a filebuf is in put mode, pbase() is non-NULL and equal to base().
  38. // Also, epptr() == ebuf().
  39. // Also, eback() == gptr() && gptr() == egptr().
  40. // The un-flushed character are those between pbase() and pptr().
  41. // GET MODE:
  42. // If a filebuf is in get or putback mode, eback() != egptr().
  43. // In get mode, the unread characters are between gptr() and egptr().
  44. // The OS file position corresponds to that of egptr().
  45. // PUTBACK MODE:
  46. // Putback mode is used to remember "excess" characters that have
  47. // been sputbackc'd in a separate putback buffer.
  48. // In putback mode, the get buffer points to the special putback buffer.
  49. // The unread characters are the characters between gptr() and egptr()
  50. // in the putback buffer, as well as the area between save_gptr()
  51. // and save_egptr(), which point into the original reserve buffer.
  52. // (The pointers save_gptr() and save_egptr() are the values
  53. // of gptr() and egptr() at the time putback mode was entered.)
  54. // The OS position corresponds to that of save_egptr().
  55. //
  56. // LINE BUFFERED OUTPUT:
  57. // During line buffered output, pbase()==base() && epptr()==base().
  58. // However, ptr() may be anywhere between base() and ebuf().
  59. // This forces a call to filebuf::overflow(int C) on every put.
  60. // If there is more space in the buffer, and C is not a '\n',
  61. // then C is inserted, and pptr() incremented.
  62. //
  63. // UNBUFFERED STREAMS:
  64. // If a filebuf is unbuffered(), the _shortbuf[1] is used as the buffer.
  65.  
  66. #ifdef atarist
  67. extern "C" { extern int __default_mode__; }
  68. #endif
  69.  
  70. void filebuf::init()
  71. {
  72.     _fb._fileno = -1;
  73.     _fb._offset = 0;
  74.     _fb._fake = 0;
  75.     _flags |= _S_IS_FILEBUF;
  76.     _fb._save_gptr = NULL;
  77.  
  78.     // Link in:
  79.     _chain = __stream_list;
  80.     __stream_list = this;
  81.     _fb._fileno = -1;
  82. }
  83.  
  84. filebuf::filebuf()
  85. {
  86.     init();
  87. }
  88.  
  89. filebuf::filebuf(int fd)
  90. {
  91.     init();
  92.     attach(fd);
  93. #ifdef atarist
  94.     if((fd == 0) || (fd == 1) || (fd == 2))
  95.     _flags &= (~_S_IS_BINARY);
  96. #endif    
  97. }
  98.  
  99. filebuf::filebuf(int fd, char* p, size_t len)
  100. {
  101.     init();
  102.     attach(fd);
  103.     setbuf(p, len);
  104. #ifdef atarist
  105.     if((fd == 0) || (fd == 1) || (fd == 2))
  106.     _flags &= (~_S_IS_BINARY);
  107. #endif    
  108. }
  109.  
  110. filebuf::~filebuf()
  111. {
  112.     if (!(_flags & _S_DELETE_DONT_CLOSE))
  113.     close();
  114.  
  115.     // Unlink:
  116.     streambuf **f;
  117.     for (f = &__stream_list; *f != NULL; f = &(*f)->_chain) {
  118.     if (*f = this) {
  119.         *f = _chain;
  120.         break;
  121.     }
  122.     }
  123. }
  124.  
  125. filebuf* filebuf::open(const char *filename, int mode, int prot = 0664)
  126. {
  127.     if (is_open())
  128.     return NULL;
  129.     int posix_mode;
  130.     int read_write;
  131.     if ((mode & (ios::in|ios::out)) == (ios::in|ios::out))
  132.     posix_mode = O_RDWR, read_write = _S_CAN_READ+_S_CAN_WRITE;
  133.     else if (mode & (ios::out|ios::app))
  134.     posix_mode = O_WRONLY, read_write = _S_CAN_WRITE;
  135.     else if (mode & (int)ios::in)
  136.     posix_mode = O_RDONLY, read_write = _S_CAN_READ;
  137.     else
  138.     posix_mode = 0, read_write = 0;
  139.     if ((mode & (int)ios::trunc) || mode == (int)ios::out)
  140.     posix_mode |= O_TRUNC;
  141.     if (mode & ios::app)
  142.     posix_mode |= O_APPEND;
  143.     if (!(mode & (int)ios::nocreate) && mode != ios::in)
  144.     posix_mode |= O_CREAT;
  145.     if (mode & (int)ios::noreplace)
  146.     posix_mode |= O_EXCL;
  147.     int fd = ::open(filename, posix_mode, prot);
  148.     if (fd < 0)
  149.     return NULL;
  150.     _fb._fileno = fd;
  151.     _fb._offset = 0;
  152.     _fb._fake = 0;
  153.     _flags |= read_write;
  154. #ifdef atarist
  155.     if(mode & 128)
  156.     _flags |= _S_IS_BINARY;
  157.     else if(mode & 256)
  158.     _flags &= (~_S_IS_BINARY);
  159. #endif    
  160.     if (mode & ios::ate) {
  161.     if (seekoff(0, ios::end) == EOF)
  162.         return NULL;
  163.     }
  164.     return this;
  165. }
  166.  
  167. filebuf* filebuf::open(const char *filename, const char *mode)
  168. {
  169.     if (is_open())
  170.     return NULL;
  171.     int oflags = 0, omode;
  172.     int read_write;
  173.     int oprot = 0666;
  174. #ifdef atarist
  175.     int text_mode = 0;
  176. #endif    
  177.     switch (*mode++) {
  178.       case 'r':
  179.     omode = O_RDONLY;
  180.     read_write = _S_CAN_READ;
  181.     break;
  182.       case 'w':
  183.     omode = O_WRONLY;
  184.     oflags = O_CREAT|O_TRUNC;
  185.     read_write = _S_CAN_WRITE;
  186.     break;
  187.       case 'a':
  188.     omode = O_WRONLY;
  189.     oflags = O_CREAT|O_APPEND;
  190.     read_write = _S_CAN_WRITE;
  191.     break;
  192.       default:
  193.     errno = EINVAL;
  194.     return NULL;
  195.     }
  196. #ifndef atarist
  197.     if (mode[0] == '+' || (mode[0] == 'b' && mode[1] == '+')) {
  198.     omode = O_RDWR;
  199.     read_write = _S_CAN_READ+_S_CAN_WRITE;
  200.     }
  201. #else
  202.     while(*mode)
  203.     {
  204.     switch(*mode++)
  205.     {
  206.       case '+':
  207.         omode = O_RDWR;
  208.         read_write = _S_CAN_READ+_S_CAN_WRITE;
  209.         break;
  210.       case 'b':
  211.         text_mode = 2;
  212.         break;
  213.       case 't':
  214.         text_mode = 1;
  215.         break;
  216.       default:
  217.         errno = EINVAL;
  218.         return NULL;
  219.     }
  220.     }
  221. #endif
  222.  
  223.     int fdesc = ::open(filename, omode|oflags, oprot);
  224.     if (fdesc < 0)
  225.     return NULL;
  226.     _fb._fileno = fdesc;
  227.     _fb._offset = 0;
  228.     _fb._fake = 0;
  229.     _flags |= read_write;
  230. #ifdef atarist
  231.     if(text_mode == 2)
  232.     _flags |= _S_IS_BINARY;
  233.     else if(text_mode == 1)
  234.     _flags &= (~_S_IS_BINARY);
  235. #endif    
  236.     return this;
  237. }
  238.  
  239. filebuf* filebuf::attach(int fd)
  240. {
  241.     if (is_open())
  242.     return NULL;
  243.     _fb._fileno = fd;
  244.     _flags |= (_S_CAN_READ+_S_CAN_WRITE+_S_DELETE_DONT_CLOSE);
  245.  
  246.     return this;
  247. }
  248.  
  249. int filebuf::overflow(int c)
  250. {
  251.     if (pptr() == pbase() && c == EOF)
  252.     return 0;
  253.     if ((_flags & _S_CAN_WRITE) == 0) // SET ERROR
  254.     return EOF;
  255.     if (is_reading()) {
  256.     if (pptr() != gptr() && pptr() > pbase())
  257.         if (do_flush())
  258.         return EOF;
  259.     setp(gptr(), ebuf());
  260.     setg(egptr(), egptr(), egptr());
  261.     }
  262.     if (allocate() > 0) {
  263.     if (_flags & _S_LINE_BUF) setp(base(), base());
  264.     else setp(base(), ebuf());
  265.     setg(pbase(), pbase(), pbase());
  266.     }
  267.     int flush_only;
  268.     if (c == EOF) flush_only = 1, c = 0;
  269.     else flush_only = 0;
  270.     if (epptr() == pbase()) { // Line buffering
  271.     if (pptr() < ebuf() && !flush_only) {
  272.         *_pptr++ = c;
  273.         if (c != '\n')
  274.         return (unsigned char)c;
  275.         else
  276.         flush_only = 1;
  277.     }
  278.     }
  279.     size_t to_do = out_waiting();
  280.     if (to_do > 0) {
  281.     char *ptr = pbase();
  282.     for (;;) {
  283.         long count = ::write(fd(), ptr, to_do);
  284.         if (count == EOF)
  285.         return EOF;
  286.         _fb._offset += count;
  287.         to_do -= count;
  288.         if (to_do == 0)
  289.         break;
  290.         ptr += count;
  291.     }
  292.     if (_flags & _S_LINE_BUF) setp(pbase(), pbase());
  293.     else setp(pbase(), epptr());
  294.     setg(egptr(), egptr(), egptr());
  295.     }
  296.     if (flush_only)
  297.     return c;
  298.     else
  299.     return sputc(c);
  300. }
  301.  
  302. int filebuf::underflow()
  303. {
  304. #if 0
  305.     /* SysV does not make this test; take it out for compatibility */
  306.     if (fp->_flags & __SEOF)
  307.     return (EOF);
  308. #endif
  309.  
  310.     if ((_flags & _S_CAN_READ) == 0) // SET ERROR
  311.     return EOF;
  312.   retry:
  313.     if (gptr() < egptr())
  314.     return *(unsigned char*)gptr();
  315.     if (_fb._save_gptr) { // Free old putback buffer.
  316.     if (eback() != _fb._shortbuf)
  317.         free(eback());
  318.     _fb._save_gptr = NULL;
  319.     setg(base(), _fb._save_gptr, _fb._save_egptr); // Restore get area.
  320.     goto retry;
  321.     }
  322.  
  323.     allocate();
  324. #if 0
  325.     /* if not already reading, have to be reading and writing */
  326.     if ((fp->_flags & __SRD) == 0) {
  327.     if ((fp->_flags & __SRW) == 0)
  328.         return (EOF);
  329.     /* switch to reading */
  330.     if (fp->_flags & __SWR) {
  331.         if (fflush(fp))
  332.         return (EOF);
  333.         fp->_flags &= ~__SWR;
  334.         fp->_w = 0;
  335.         fp->_lbfsize = 0;
  336.     }
  337.     fp->_flags |= __SRD;
  338.     } else {
  339.     // We were reading.  If there is an ungetc buffer,
  340.     // we must have been reading from that.  Drop it,
  341.     // restoring the previous buffer (if any).  If there
  342.     // is anything in that buffer, return.
  343.     if (HASUB(fp)) {
  344.         FREEUB(fp);
  345.         if ((fp->_r = fp->_ur) != 0) {
  346.         fp->_p = fp->_up;
  347.         return (0);
  348.         }
  349.     }
  350.     }
  351. #endif
  352.     if ((_flags & _S_LINE_BUF) || unbuffered()) {
  353.     // Flush all line buffered files before reading.
  354.     for (streambuf* fp = __stream_list; fp != NULL; fp = fp->_chain)
  355.         if (fp->linebuffered())
  356.         fp->sync();
  357.     }
  358.     if (pptr() > pbase())
  359.     if (do_flush()) return EOF;
  360.     int count = sys_read(base(), ebuf() - base());
  361.     if (count <= 0) {
  362.     if (count == 0)
  363.         _flags |= _S_EOF_SEEN;
  364.     else
  365.         _flags |= _S_ERR_SEEN, count = 0;
  366.     return EOF;
  367.     }
  368.     setg(base(), base(), base() + count);
  369.     return *(unsigned char*)gptr();
  370. }
  371.  
  372. int filebuf::pbackfail(int c)
  373. {
  374.     if (is_writing()) {
  375.     overflow(EOF);
  376.     // FIXME: check for writing!
  377.     }
  378.     if (_fb._save_gptr == NULL) { // No putback buffer.
  379.     _fb._save_gptr = gptr(); _fb._save_egptr = egptr();
  380.     setg(_fb._shortbuf, _fb._shortbuf+1, _fb._shortbuf+1);
  381.     }
  382.     else { // Increase size of existing putback buffer.
  383.     size_t new_size;
  384.     size_t old_size = egptr() - eback();
  385.     new_size = eback() == _fb._shortbuf ? 128 : 2 * old_size;
  386.     char* new_buf = new char[new_size];;
  387.     memcpy(new_buf+(new_size-old_size), eback(), old_size);
  388.     if (eback() != _fb._shortbuf)
  389.         delete eback();
  390.     setg(new_buf, new_buf+(new_size-old_size), new_buf+new_size);
  391.     }
  392.     gbump(-1);
  393.     *gptr() = c;
  394.     return (unsigned char)c;
  395. }
  396.  
  397. int filebuf::do_flush()
  398. {
  399.     if (egptr() != pbase()) {
  400.     long new_pos = sys_seek(pbase()-egptr(), ios::cur);
  401.     if (new_pos == -1)
  402.         return EOF;
  403.     }
  404.     long to_do = pptr()-pbase();
  405.     char* ptr = pbase();
  406.     while (to_do > 0) {
  407.     long count = sys_write(ptr, to_do);
  408.     if (count == EOF)
  409.         return EOF;
  410.     if (_fb._offset >= 0)
  411.         _fb._offset += count;
  412.     to_do -= count;
  413.     ptr += count;
  414.     }
  415.     setg(base(), pptr(), pptr());
  416.     setp(NULL, NULL); // ????
  417.     return 0;
  418. }
  419.  
  420. int filebuf::sync()
  421. {
  422. //    char* ptr = cur_ptr();
  423.     if (pptr() > pbase())
  424.     do_flush();
  425.     if (gptr() != egptr()) {
  426.     if (sys_seek(gptr() - egptr(), ios::cur) == EOF)
  427.         return EOF;
  428.     }
  429.     // FIXME: Handle putback mode!
  430. //    setg(base(), ptr, ptr);
  431.     return 0;
  432. }
  433.  
  434. streamoff filebuf::seekoff(streamoff offset, seek_dir dir, int mode)
  435. {
  436. #ifdef atarist
  437.     long result, new_offset, delta;
  438. #else
  439.     fpos_t result, new_offset, delta;
  440. #endif
  441.     int count;
  442.     if (unbuffered())
  443.     goto dumb;
  444.     // FIXME: WHat if buffer already allocated?
  445.     switch (dir) {
  446.       case ios::cur:
  447.     if (_fb._offset < 0) {
  448.         _fb._offset = sys_seek(0, ios::cur);
  449.         if (_fb._offset < 0)
  450.         return EOF;
  451.     }
  452.     offset += _fb._offset + (file_ptr()-base());
  453.     if (_fb._save_gptr)
  454.         offset -= egptr() - gptr(); // Subtract putback size.
  455.     dir = ios::beg;
  456.     break;
  457.       case ios::beg:
  458.     break;
  459.       case ios::end:
  460.     struct stat st;
  461.     if (sys_stat(&st) == 0 && (st.st_mode & S_IFMT) == S_IFREG) {
  462.         offset += st.st_size;
  463.         dir = ios::beg;
  464.     }
  465.     else
  466.         goto dumb;
  467.     }
  468.     // At this point, dir==ios::beg.
  469.     // FIXME: Handle case of there being a putback buffer!
  470.     // If destination is within current buffer, optimize:
  471.     if (_fb._offset >= 0) {
  472.     long rel_offset = (long)(offset - _fb._offset); // Offset relative to base().
  473.     if (rel_offset >= 0
  474.         // should be: <= max(egptr(),pptr()-base() ???
  475.         && rel_offset <= (long)(egptr()-base())) {
  476.         setg(base(), base() + rel_offset, egptr());
  477.         return offset;
  478.         }
  479.     }
  480.     if (pptr() > pbase()) {
  481.     do_flush();
  482.     }
  483.     // Try to seek to a block boundary, to improve kernal page management.
  484.     new_offset = offset & ~(ebuf() - base() - 1);
  485.     delta = offset - new_offset;
  486.     if (delta > ebuf() - base()) {
  487.     new_offset = offset;
  488.     delta = 0;
  489.     }
  490.     result = sys_seek(new_offset, ios::beg);
  491.     if (result < 0)
  492.     return EOF;
  493.     _fb._offset = result;
  494.     count = sys_read(base(), ebuf()-base());
  495.     if (count < 0)
  496.     return EOF;
  497.     setg(base(), base(), base()+count);
  498.     _flags &= ~ _S_EOF_SEEN;
  499.     return result;
  500.   dumb:
  501.     // Maybe just do sync?
  502.     if (pptr() > pbase()) {
  503.     do_flush();
  504.     }
  505.     if (_fb._save_gptr != NULL) { // Get rid of putback buffer.
  506.     if (eback() != _fb._shortbuf)
  507.         free(eback());
  508.     _fb._save_gptr = NULL;
  509.     }
  510.     result = sys_seek(offset, dir);
  511.     if (result != EOF) {
  512.     _flags &= ~_S_EOF_SEEN;
  513.     }
  514.     char* start = unbuffered() ? _fb._shortbuf : base();
  515.     setg(start, start, start);
  516.     setp(start, start);
  517.     return result;
  518. }
  519.  
  520. filebuf* filebuf::close()
  521. {
  522.     if (!is_open())
  523.     return NULL;
  524.  
  525.     // Flush.
  526.     overflow(EOF);
  527.  
  528.     /* Free the buffer's storage.  */
  529.     // Or should that be done only on destruction???
  530.     if (_base != NULL && !(_flags & _S_USER_BUF)) {
  531.     free(_base);
  532.     _base = NULL;
  533.     }
  534.  
  535.     int status = sys_close();
  536.  
  537.     _flags = 0;
  538.     _fb._fileno = EOF;
  539.  
  540.     return status < 0 ? NULL : this;
  541. }
  542.  
  543. #ifndef atarist
  544. int filebuf::sys_read(char* buf, size_t size)
  545. {
  546.     return ::read(_fb._fileno, buf, size);
  547. }
  548. fpos_t filebuf::sys_seek(fpos_t offset, seek_dir dir)
  549. {
  550.     return ::lseek(fd(), offset, (int)dir);
  551. }
  552.  
  553. #else
  554. long filebuf::sys_read(char* buf, size_t size)
  555. {
  556.     return ::_read(_fb._fileno, buf, size);
  557. }
  558.  
  559. long filebuf::sys_seek(long offset, seek_dir dir)
  560. {
  561.     return ::lseek(fd(), offset, (int)dir);
  562. }
  563. #endif
  564.  
  565.  
  566. long filebuf::sys_write(const void *buf, long n)
  567. {
  568. #ifndef atarist
  569.     return ::write(fd(), buf, n);
  570. #else
  571.     return ::_write(fd(), buf, n);
  572. #endif
  573. }
  574.  
  575. int filebuf::sys_stat(void* st)
  576. {
  577.     return ::_fstat(fd(), (struct stat*)st);
  578. }
  579.  
  580. int filebuf::sys_close()
  581. {
  582.     return ::close(fd());
  583. }
  584.